In the field of software development, program code is often developed in separate modules. The separate program code modules are then combined, or linked, during a compilation process that generates an executable application. For example, as shown in FIG. 1, several independent source code files (e.g., files 10, 12, 14 and 16), each including one or more independent program code modules, are converted to object files (e.g, 18, 20, 22 and 24) before a linker 26 links the object files to generate an executable file 28. If necessary, the linker 26 may also link one or more code modules from a runtime library 30. There are a variety of often-cited advantages to developing code in this modular approach. One such advantage is reusability. For instance, a particular source code module that is associated with a particular aspect or feature set of a hardware device may be used in generation after generation of that hardware device with few, if any, changes.
Despite the many well-known advantages to the modular approach to developing program code, one of the difficulties with the modular approach is identifying and managing the various external program code dependencies associated with a particular code base. Given a particular program code module, an external program code dependency exists where, in order to compile, execute and/or function properly, that particular program code module is dependent upon an external program code module. In the case of the popular C programming language, external program code dependencies arise in one of two situations. In the C programming language, program code dependencies arise when program code is included by using the “#include” pre-processor directive, and when “identifiers” (e.g., macros, variables, functions, types, and labels) are referenced, but not defined, in the code base.
Being able to quickly and accurately identify all external program code dependencies is important, particularly when trying to port complex software from one operating system to another. When porting software from one operating system to another, identifying external program code dependencies early in the development process allows the software developer to re-implement the features or behaviors required for a particular program code module to work with the target system, or to re-work the base software code so as not to require the particular features or behaviors provided by the external code.
A variety of techniques and tools exist (with varying levels of efficiency and effectiveness) for identifying external program code dependencies. Many existing techniques and tools fail to identify all types of external program code dependencies. Furthermore, existing tools and techniques often fail to accurately identify the particular file containing the program code on which the code base depends. For example, one way to identify external program code dependencies, such as those associated with “#include” pre-processor directives, is to simply search through the code base for the “#include” statements, and then cross reference the files indicated in the code base with a known list of files. Generally, this method is effective in identifying the existence of external program code dependencies; however, this method is subject to error in determining the proper file on which the code base depends. This is in part due to the specific compilation parameters used by compilers, and the possibility of complicated include paths that are setup by makefiles used in compiling the code base. Accordingly, knowledge of the software's compilation parameters is necessary to produce an accurate list of files containing program code on which the code base depends.
Likewise, determining an identifier's correct definition is often problematic. In the C programming language, a set of reserved words, operators, and separators are defined. All other text strings within the base code are either integer constants or identifiers, and differentiating between the two is as simple as determining whether the text string begins with a numeric character. By definition, C identifiers cannot begin with a numeric character. However, as illustrated in the code examples in FIG. 2, identifying the existence of an external program code dependency, and identifying the program code module on which the code base depends, is often a difficult task.
Example 1 in FIG. 2 illustrates the difficulty in determining the proper definition for a C identifier. In the code of Example 1 (FIG. 2), a structure of type bar is defined. However, the definition for the particular structure is generated using a macro. Consequently, given only line 2 of the code, it is not possible to recognize that the structure “my_bar” is being defined. Accordingly, searching for and finding the proper definition is not as simple as it might seem.
As illustrated in the code of Examples 2 and 3 in FIG. 2, the definition of a reference may vary depending on the scope and namespace, respectively. For example, as illustrated in Example 2, the identifier ‘foo’ is incremented in two different locations. One of the “foo” variables is locally defined, while the other is not. Accordingly, the definition of the variable is dependent upon the scope. Similarly, in example 3, the variable “foo” is referenced in two different namespaces. These examples illustrate why, in many cases, manually determining the definition of a reference is difficult without careful examination of the code, because often the definition referenced by an identifier varies depending on the scope and namespace respectively.
As illustrated in Example 4 of FIG. 2, a compile time parameter determines which definition is ultimately used for the identifier “foo”. Accordingly, the type and/or definition used for a particular identifier can change depending on one or more compile time parameters. This illustrates that compilation parameters can affect the definition of an identifier in complex ways. Therefore, producing a correct list of external program code dependencies is not a simple task and generally requires an understanding of programming language and compilation process and parameters.